<template>
  <div id="graphContainer"></div>
</template>
<script>
import G6 from "@antv/g6";
export default {
  props: {
    dataset: {
      type: Object,
      default: () => {
        return {
          nodes: [],
          edges: [],
        };
      },
    },
  },
  data() {
    return {
      width: 0,
      height: 0,
      graph: null,
      defaultNodeRadius: 32,
    };
  },
  methods: {
    /**
     * @description: 单行文字超长时用省略号替换
     * @param {*} str       需要转换文本
     * @param {*} maxWidth  单行最大长度
     * @param {*} fontSize  字体大小
     * @return {*}
     */
    fittingStringByEllipsis(str, maxWidth, fontSize) {
      const ellipsis = "...";
      const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0];
      let currentWidth = 0;
      let res = str;
      const pattern = new RegExp("[\u4E00-\u9FA5]+"); // distinguish the Chinese characters and letters
      str.split("").forEach((letter, i) => {
        if (currentWidth > maxWidth - ellipsisLength) return;
        if (pattern.test(letter)) {
          currentWidth += fontSize;
        } else {
          // get the width of single letter according to the fontSize
          currentWidth += G6.Util.getLetterWidth(letter, fontSize);
        }
        if (currentWidth > maxWidth - ellipsisLength) {
          res = `${str.substr(0, i)}${ellipsis}`;
        }
      });
      return res;
    },
    transferLabel(nodes) {
      nodes.forEach((item) => {
        let label = this.fittingStringByEllipsis(item.name, 150, 14);
        item.subLabel = item.name;
        item.label = label;
      });
    },
    transferGraphData(nodes, edges) {
      let _nodes = nodes.map((node) => {
        let label = this.fittingStringByEllipsis(node.name, 150, 14);
        let subLabel = node.name;
        let selected = node.color;
        let color = selected ? selected.color : "#ff00ff";
        return {
          id: node.id + "",
          entity_id: node.entity_id,
          label,
          subLabel,
          style: { fill: color },
          color,
        };
      });
      let _edges = edges.map((edge) => {
        return {
          id: edge.id + "",
          source: edge.source + "",
          target: edge.target + "",
          label: edge.label,
        };
      });
      return { nodes: _nodes, edges: _edges };
    },
    // 获取图谱容器的宽高
    getGraphContainerCon() {
      const graphCon = document.getElementById("graphContainer");
      this.width = graphCon.offsetWidth;
      this.height = graphCon.offsetHeight;
    },
    initGraph() {
      const tooltip = new G6.Tooltip({
        offsetX: 10,
        offsetY: 0,
        // v4.2.1 起支持 fixToNode，tooltip 相对于节点固定位置
        fixToNode: [1, 0],
        // the types of items that allow the tooltip show up
        // 允许出现 tooltip 的 item 类型
        itemTypes: ["node"],
        // custom the tooltip's content
        // 自定义 tooltip 内容
        getContent: (e) => {
          let label = e.item.getModel().subLabel;
          if (label) {
            const outDiv = document.createElement("div");
            outDiv.style.width = "fit-content";
            outDiv.style.padding = "0px 0px 0px 0px";
            outDiv.innerHTML = `
                    <p class="node-label" style="max-width:200px;">${label}</p>
                    `;
            return outDiv;
          }
          return null;
        },
      });
      this.graph = new G6.Graph({
        container: "graphContainer",
        width: this.width,
        height: this.height,
        plugins: [tooltip],
        renderer: "canvas",
        animate: true,
        enabledStack: true,
        linkCenter: true,
        layout: {
          type: "force",
          linkDistance: 250,
          nodeStrength: 10,
          nodeSize: 50,
          preventOverlap: true, // 防止节点覆盖
        },
        // 交互模式：包括节点和边的信息浮框, 缩放/拖拽画布、节点等
        modes: {
          default: [
            "drag-canvas",
            "drag-node",
            "zoom-canvas",
            {
              type: "brush-select",
              trigger: "shift",
            },
          ],
          "box-select": ["brush-select", "drag-node"],
          "create-edge": ["drag-canvas", "drag-node", "zoom-canvas"],
        },
        defaultNode: {
          size: 40,
          type: "circle-animate",
          style: {
            fill: "l(270) 0:#7dc4ee 1:#1d75a8", // 填充色
            stroke: "#BFC0C0", // 描边颜色
            lineWidth: 0, // 描边宽度
            cursor: "pointer",
          },
          labelCfg: {
            position: "bottom", //"center"为内嵌，"bottom"为置底
            style: {
              fill: "#fff",
              fontSize: 14,
              fontWeight: 400,
            },
          },
        },
        defaultEdge: {
          type: "line",
          style: {
            lineWidth: "1",
            stroke: "#00AAFF",
            endArrow: {
              // 设置终点箭头
              path: G6.Arrow.vee(10, 12, 15), // 使用内置箭头路径函数，参数为箭头的 宽度、长度、偏移量（默认为 0，与 d 对应）
              d: 10,
              fill: "#00AAFF",
            },
          },
          labelCfg: {
            autoRotate: true,
            refY: 10,
            style: {
              fill: "#00AAFF",
            },
          },
        },
        nodeStateStyles: {
          hover: {
            fill: "lightsteelblue",
          },
          click: {
            stroke: "#fff",
            lineWidth: 2,
          },
        },
        edgeStateStyles: {
          click: {
            stroke: "#FFB997",
          },
        },
      });
      this.registerEvent();
    },
    setData(nodes, edges) {
      G6.Util.processParallelEdges(edges);
      this.graph.data({
        nodes: nodes,
        edges: edges,
      }); // 读取远程数据到图上
      this.graph.render();
      const zoom = this.graph.getZoom();
    },
    registerEvent() {
      this.graph.on("wheelzoom", (zoomVal) => {
        if (zoomVal >= 9.15) {
        } else {
          this.zoomVal = zoomVal;
        }
      });
      window.onresize = () => {
        if (!this.graph || this.graph.get("destroyed")) return;
        this.getGraphContainerCon();
        this.graph.changeSize(this.width, this.height);
      };
      this.graph.on("node:mouseenter", async (e) => {
        const node = e.item;
        this.graph.updateItem(node, {
          style: {
            stroke: "#fff",
            lineWidth: 2,
          },
        });
      });
      this.graph.on("node:mouseleave", async (e) => {
        const node = e.item;
        this.graph.updateItem(node, {
          style: {
            stroke: "#fff",
            lineWidth: 0,
          },
        });
      });
    },
  },
  watch: {
    dataset: {
      handler(val) {
        let { nodes, edges } = this.transferGraphData(val.nodes, val.edges);
        if (!this.graph) {
          this.$nextTick(() => {
            if (!this.width || !this.height) {
              this.getGraphContainerCon();
            }
            if (this.width && this.height) {
              this.initGraph();
            }
            window.onresize = () => {
              if (!this.graph || this.graph.get("destroyed")) return;
              this.getGraphContainerCon();
              this.graph.changeSize(this.width, this.height);
            };
            this.setData(nodes, edges);
          });
        } else {
          this.setData(nodes, edges);
        }
      },
      // immediate: true,
      deep: true,
    },
  },
};
</script>
<style lang="scss">
#graphContainer {
  width: 100%;
  height: 100%;
  // position: relative;

  .dom-circle {
    width: 100%;
    height: 100%;
    border-radius: 50%;
  }
  .g6-component-tooltip {
    background: rgba(17, 64, 185, 0.8) !important;
    box-shadow: 0px 2px 4px 4px rgba(0, 0, 0, 0.08) !important;
    border: 0 none;
    font-size: 12px;
    font-family: PingFangSC-Regular, PingFang SC;
    font-weight: 400;
    color: #ffffff;
    line-height: 17px;
  }
}
</style>
